#pragma once

#include <map>

#include "pmchunk.h"
#include "file.h"
#include "slab.h"
#include "../util/concurrentqueue.h"

namespace kvalloc {

using std::map;

// supported size classes
const map<int16_t, int> SIZECLASS = {
    {64, 0},    // 64 objects per page
    {96, 1},    // 43 objects per page
    {128, 2},   // 32 objects per page
    {192, 3},   // 21 objects per page
    {256, 4},   // 16 objects per page
    {341, 5},   // 12 objects per page
    {512, 6},  // 8 objects per page
    {682, 7},  // 6 objects per page
    {1024, 8}, // 4 objects per page
    {1365, 9}, // 3 objects per page
    {2048, 10}, // 2 objects per page
    {3072, 11}, // 2 objects per page
    {4096, 12}, // 2 objects per page
};
constexpr int SIZECLASS_NUM = 13;
constexpr int MIN_SIZECLASS = 64;
constexpr int MAX_SIZECLASS = 4096;

// NVM Meta Wrapper
struct NVMetaWrapper {
    SlabList alloc_sm[SIZECLASS_NUM];  // lists of in-allocating slabs for all size classes
    SlabList free_sm[SIZECLASS_NUM];   // lists of in-freeing slabs for all size classes
    SlabMeta all_sm[0];                // meta data of each slab
};

// allocator for KV objects, manages small-grained slot on SSD files 
class KVAlloc {
public:
    static const int ALLOC_BATCH = 32;   // batch size for allocating pages from file
    // volatile states
    uint64_t max_snum_;
    DataFile file_; // the database file
    
    // persistent states
    PMChunk chunk_;   // a piece of Non-Volatile Memory located on NVM devices
    NVMetaWrapper * nvmeta_; // a mirror of chunk_, which refelects the meta data of KVAlloc

    KVAlloc(const std::string & dbfname, const std::string & metafname, uint64_t max_pnum = 256UL * 1024);

    /// @brief Allocate a empty slot from SSD that can accommodate an object of len bytes 
    /// @param len size of the object to store
    /// @return file address of the allocated slot 
    FileAddr Alloc(size_t len);

    /// @brief Free an occupied slot starting at FileAddr addr
    /// @param addr starting address of the object to delete 
    void Free(FileAddr addr);

    // read a file addr
    void Read(char * buf, FileAddr addr);
    
    // 
    SlabMeta * at(uint64_t pid) {
        assert(pid < max_snum_); 
        return &(nvmeta_->all_sm[pid]);
    }

    // get the size of a object
    size_t GetSize(FileAddr addr);
};

} // kvalloc

